中間者攻撃を防ぐためのPiping Chunk 0.6.x以降の認証方式の流れ
はじめに
0.6.0で下記の問題を解決した認証方式に変更した。
良いことに、UIは変わらないのでユーザーは使用法はそのままで、安全性が向上する。
Verification Code生成での通信内容が変わるので開発者ツールのNetworkを見ると送り合うJSONが変わっている。(公開情報交換時は暗号化はされない。そもそも暗号化するための交換。)
0.6.x以降の認証方式
全体像を捉えるための説明。
通信の流れ
流れを書くことで脆弱な箇所があった場合、あとから分かりやすくなること期待。
登場人物は送信者Sと受信者R。
送信者Sと受信者RはRSAのキーペアを生成する
公開鍵をそれぞれ$ Rpub_Sと$ Rpub_Rとする。
秘密鍵ををそれぞれ$ Rpriv_Sと$ Rpriv_Rとする。
送信者Sと受信者Rは楕円曲線ディフィー・ヘルマン鍵共有用のキーペアを生成する
公開鍵をそれぞれ$ Epub_Sと$ Epub_Rとする。
送信者Sは$ Rpriv_Sで$ Epub_Sを署名。受信者Rは$ Rpriv_Rで$ Epub_Rを署名する
送信者の署名を$ Sign_S、受信者の署名を$ Sign_Rとする。
送信者Sは$ Rpub_Sと$ Epub_Sと$ Sign_Sを受信者Rに送る
受信者Rは$ Rpub_Rと$ Epub_Rと$ Sign_Rを送信者Sに送る(上記と同様)
Verification CodeはRSAの公開鍵たちのフィンガープリントを使っている(詳細は以下の手順)
まず、送信者と受信者RSAの公開鍵たちのJWKのThumbprintにする(hex形式)
そのThumbprintを辞書順でソートして'-'で連結する
その連結したものをSHA-256のhex形式で算出する
上位32文字をVerification Codeとして使う
まとめると$ sha256(sort([ thumbprint(Rpub_S), thumbprint(Rpub_R)]).join('-')).take(32) みたいに生成している
ソートする理由:
ソートせずに${送信者}-${受信者}のふうに順番を決めれば一意に決まるのになぜしなかったか?
この鍵交換部分の関数は共通化されていて、送信者も受信者も同じ方法でこの関数を利用する。
つまり、関数は送信者から呼ばれたのか受信者から呼ばれたのかを知らない。知らないほうがシンプルで一貫性があると判断した。
そのためソートして一意に順番が定まるようになっている
Verification Codeが送信者と受信者で一致しているかを確認して相手が正しいか確認する
一致の確認はPiping Chunkの性質上、通信相手が隣にいたり、自分自身で他の端末に送る場合だったり、電話越しで話してたりするので、それを信用することになる。
$ Epub_Sや$ Epub_Rを使うことで楕円曲線ディフィー・ヘルマン鍵共有で共通鍵は手に入る
その共通鍵でこれ以後の通信は暗号化できる
上記で登場する鍵はいずれもエフェメラルである。使い捨て。
リプレイしようとしても同じ鍵が二度とこないと言える(確率論的に)ので、リプレイできないと思う。
通信相手の公開鍵のフィンガープリントを使うのはGPGでも使わている方法で信頼いいと思っている。 Verification Codeの一致ができたら、送信者VERIFYボタンを押すと発生するリクエストに関して。
コード内ではverificationと呼んでいるが、acceptanceとか変更したほうが良さそうな気がしてきた。理由は署名に対する検証のverificationと用語が混同しそうだから。
送信者の許可リクエストは{verified: true}とか{verified: false}なBodyが暗号化されて送信される。これをなりすます問題もある。ただ、これをなりすましてできることは、送信者が許可してないのに送信しようとしていると誤情報を送ったり、許可する予定だったのに先を越して、許可しないという誤情報を流して受信者の受信をあきらめさせること。つまり、データが漏れることとは無関係。中間者攻撃して悪意のあるデータを受信者に流し込むことはできると思う。Verification Codeが違うことがすぐに分かり中間者攻撃に気付けるし、ファイルを開かなければ悪意のあるコードなどが実行されることはないと思う。これらを回避したいなら、受信者にもVERIFYボタンを用意して、VERIFYが押されたあとに受信のGETをし始めるような実装ができると思う。ただ、利便性のため現在はこれを実装しない。情報漏えいが防げていれば問題ないという実装。今後の経験で受信者もVERIFYできる方が良いと経験をすればそういう実装に変わるのだと思う。